use common_base::{
    dashmap::DashMap,
    futures::TryStreamExt,
    prelude::*,
    sqlx::{self, mysql::MySqlArguments, Arguments, MySql},
};
use common_core::{
    data::{DatasetOwner, DatasetType, DatasetUtil, DoMetadata, Metadata, RowsetOwner},
    ServiceContextBase,
};

use crate::MetadataManager;

use super::metadata_util::MetadataUtil;

pub struct DoMetadataManager;

impl DoMetadataManager {
    pub async fn get_metadata_model(
        &self,
        srv_ctx: &mut ServiceContextBase,
        do_id: &str,
    ) -> Result<Option<DoMetadata>> {
        if let Some(mut do_metadata) = self.load_sys_objects(srv_ctx, do_id).await? {
            self.load_keys(srv_ctx, &mut do_metadata).await?;

            if let Some(col_dataset) = self.load_sys_objcols(srv_ctx, &mut do_metadata).await? {
                do_metadata.set_columns(col_dataset);
                self.load_fkeys(&mut do_metadata);
                // self.load_ref_object(srv_ctx, &mut do_metadata).await?;
                self.load_index(srv_ctx, &mut do_metadata).await?;
            }

            self.load_sys_obj_val(srv_ctx, &mut do_metadata).await?;

            self.load_io_metadata(srv_ctx, &mut do_metadata).await?;

            MetadataManager::process_reference_metadata(
                srv_ctx,
                do_id,
                Metadata::Do(do_metadata.clone()),
            );

            Ok(Some(do_metadata))
        } else {
            Ok(None)
        }
    }

    // 加载DataObject基本属性
    async fn load_sys_objects(
        &self,
        srv_ctx: &mut ServiceContextBase,
        do_id: &str,
    ) -> Result<Option<DoMetadata>> {
        let mut arguments = MySqlArguments::default();
        arguments.add(do_id);

        let (sql, arguments) = MetadataUtil::generate_sql_and_args(
            srv_ctx,
            "SYS_OBJECTS",
            "*",
            "OBJ_ID=?",
            "",
            arguments,
            do_id,
        );

        let mut rows = sqlx::query_with::<MySql, MySqlArguments>(sql.as_str(), arguments).fetch(
            srv_ctx
                .get_connection_mut()
                .expect("can not find db_conn in context"),
        );

        while let Some(row) = rows.try_next().await? {
            return Ok(Some(DoMetadata::new(do_id, row.try_into()?)));
        }
        Ok(None)
    }

    /// 加载主键
    async fn load_keys(
        &self,
        srv_ctx: &mut ServiceContextBase,
        do_metadata: &mut DoMetadata,
    ) -> Result<()> {
        let mut arguments = MySqlArguments::default();
        arguments.add(do_metadata.get_object_id());

        let (sql, arguments) = MetadataUtil::generate_sql_and_args(
            srv_ctx,
            "SYS_KEYS",
            "*",
            "OBJ_ID=? AND KEY_TYPE='P' ",
            "KEY_ID",
            arguments,
            do_metadata.get_object_id(),
        );

        let mut rows = sqlx::query_with::<MySql, MySqlArguments>(sql.as_str(), arguments).fetch(
            srv_ctx
                .get_connection_mut()
                .expect("can not find db_conn in context"),
        );

        let mut dataset = DatasetOwner::get_instance("SYS_KEYS", DatasetType::Container);
        dataset.set_prime_key(vec!["KEY_ID"]);

        while let Some(row) = rows.try_next().await? {
            let rowset: RowsetOwner = row.try_into()?;
            let rowset_ref = dataset.insert_rowset(rowset);
            dataset.build_prime_key_index(&rowset_ref);
        }

        do_metadata.set_keys(dataset);

        Ok(())
    }

    /// 加载列定义信息
    async fn load_sys_objcols(
        &self,
        srv_ctx: &mut ServiceContextBase,
        do_metadata: &mut DoMetadata,
    ) -> Result<Option<DatasetOwner>> {
        let mut arguments = MySqlArguments::default();
        arguments.add(do_metadata.get_object_id());

        let (sql, arguments) = MetadataUtil::generate_sql_and_args(
            srv_ctx,
            "SYS_OBJCOLS",
            "*",
            "OBJ_ID=?",
            "COL_DISP",
            arguments,
            do_metadata.get_object_id(),
        );

        let mut dataset = {
            let mut rows = sqlx::query_with::<MySql, MySqlArguments>(sql.as_str(), arguments)
                .fetch(
                    srv_ctx
                        .get_connection_mut()
                        .expect("can not find db_conn in context"),
                );

            let mut dataset = DatasetOwner::get_instance("SYS_OBJCOLS", DatasetType::Container);
            dataset.set_prime_key(vec!["COL_ID"]);

            while let Some(row) = rows.try_next().await? {
                let rowset: RowsetOwner = row.try_into()?;
                let rowset_ref = dataset.insert_rowset(rowset);
                dataset.build_prime_key_index(&rowset_ref);
            }

            dataset
        };

        // 装入编辑方式定义为自列表的列的自列表数据
        self.load_self_enum(srv_ctx, do_metadata, &mut dataset)
            .await?;

        Ok(Some(dataset))
    }

    /// 加载编辑方式定义为自列表的列的自列表数据
    async fn load_self_enum(
        &self,
        srv_ctx: &mut ServiceContextBase,
        do_metadata: &mut DoMetadata,
        dataset: &mut DatasetOwner,
    ) -> Result<()> {
        let mut arguments = MySqlArguments::default();
        arguments.add(do_metadata.get_object_id());

        let (sql, arguments) = DatasetUtil::generate_sql_and_args(
            srv_ctx,
            "SYS_SELFENUM",
            "*",
            "OBJ_ID = ? and F_SYZT = '1'",
            "",
            arguments,
        );

        let mut rows = sqlx::query_with::<MySql, MySqlArguments>(sql.as_str(), arguments).fetch(
            srv_ctx
                .get_connection_mut()
                .expect("can not find db_conn in context"),
        );

        let selfenum_map: DashMap<String, Vec<RowsetOwner>> = DashMap::default();
        while let Some(row) = rows.try_next().await? {
            let rowset: RowsetOwner = row.try_into()?;
            let col_id = rowset.get_string("COL_ID");
            if !selfenum_map.contains_key(col_id) {
                selfenum_map.insert(col_id.to_owned(), Vec::default());
            }

            selfenum_map
                .entry(col_id.to_owned())
                .or_insert(Vec::default())
                .push(rowset);
        }

        for col_rowset in dataset.get_rowset_list_mut().iter_mut() {
            let col_id = col_rowset.get_string("COL_ID");
            if let Some(mut enum_rowset_list) = selfenum_map.get_mut(col_id) {
                enum_rowset_list.sort_by(|rowset_0, rowset_1| {
                    let disp_0 = rowset_0.get_object("COL_DISP");
                    let disp_1 = rowset_1.get_object("COL_DISP");

                    disp_0.cmp(&disp_1)
                });

                let mut sys_selfenum = String::new();
                for rs in enum_rowset_list.iter() {
                    sys_selfenum.push_str(rs.get_string("COL_VAL"));
                    sys_selfenum.push(':');
                    sys_selfenum.push_str(rs.get_string("F_NOTE"));
                    sys_selfenum.push(';');
                }

                col_rowset.put_string("SYS_SELFENUM", sys_selfenum);
            }
        }

        Ok(())
    }

    /// 加载外键
    fn load_fkeys(&self, do_metadata: &mut DoMetadata) {
        let mut fkeys = Vec::default();
        if let Some(col_dataset) = do_metadata.get_columns() {
            for rowset in col_dataset.get_rowset_list() {
                let fkey_col_id = rowset.get_string("COL_ID");
                let fkey_obj_id = rowset.get_string("COL_FOBJ");
                fkeys.push((fkey_col_id.to_owned(), fkey_obj_id.to_owned()));
            }
        }

        for (fkey_col_id, fkey_obj_id) in fkeys {
            if !fkey_obj_id.trim().is_empty() {
                do_metadata.add_fkey_metadata(fkey_col_id, fkey_obj_id);
            }
        }
    }

    /// 加载索引定义
    async fn load_index(
        &self,
        srv_ctx: &mut ServiceContextBase,
        do_metadata: &mut DoMetadata,
    ) -> Result<()> {
        let mut arguments = MySqlArguments::default();
        arguments.add(do_metadata.get_object_id());

        let (sql, arguments) = MetadataUtil::generate_sql_and_args(
            srv_ctx,
            "SYS_INDEXS",
            "*",
            "OBJ_ID = ? ",
            "INX_ID",
            arguments,
            do_metadata.get_object_id(),
        );

        let mut rows = sqlx::query_with::<MySql, MySqlArguments>(sql.as_str(), arguments).fetch(
            srv_ctx
                .get_connection_mut()
                .expect("can not find db_conn in context"),
        );

        let mut dataset = DatasetOwner::get_instance("SYS_INDEXS", DatasetType::Container);
        dataset.set_prime_key(vec!["INX_ID"]);

        while let Some(row) = rows.try_next().await? {
            let rowset: RowsetOwner = row.try_into()?;
            let rowset_ref = dataset.insert_rowset(rowset);
            dataset.build_prime_key_index(&rowset_ref);
        }

        do_metadata.set_indexs(dataset);

        Ok(())
    }

    /// 加载扩展属性
    async fn load_sys_obj_val(
        &self,
        srv_ctx: &mut ServiceContextBase,
        do_metadata: &mut DoMetadata,
    ) -> Result<()> {
        let mut arguments = MySqlArguments::default();
        arguments.add(do_metadata.get_object_id());

        let (sql, arguments) = DatasetUtil::generate_sql_and_args(
            srv_ctx,
            "SYS_OBJ_VAL",
            "*",
            "OBJ_ID = ? ",
            "",
            arguments,
        );

        let mut rows = sqlx::query_with::<MySql, MySqlArguments>(sql.as_str(), arguments).fetch(
            srv_ctx
                .get_connection_mut()
                .expect("can not find db_conn in context"),
        );

        let mut dataset = DatasetOwner::get_instance("SYS_OBJ_VAL", DatasetType::Container);
        dataset.set_prime_key(vec!["OBJ_KEY"]);

        while let Some(row) = rows.try_next().await? {
            let rowset: RowsetOwner = row.try_into()?;
            let rowset_ref = dataset.insert_rowset(rowset);
            dataset.build_prime_key_index(&rowset_ref);
        }

        for rowset in dataset.get_rowset_list() {
            do_metadata
                .add_sys_obj_val(rowset.get_string("OBJ_KEY"), rowset.get_string("OBJ_VALUE"));
        }

        Ok(())
    }

    async fn load_io_metadata(
        &self,
        srv_ctx: &mut ServiceContextBase,
        do_metadata: &mut DoMetadata,
    ) -> Result<()> {
        todo!()
    }
}

// Deprecated
// impl DoMetadataManager {
//     /// Loads all the `ref object`s that a `DataObject` contains
//     async fn load_ref_object(
//         &self,
//         srv_ctx: &mut ServiceContextBase,
//         do_metadata: &mut DoMetadata,
//     ) -> Result<()> {
//         let ref_id = do_metadata.get_string("REF_ID");
//         if ref_id.is_empty() {
//             return Ok(());
//         }
//
//         // TODO:
//         // if let Some(ref_metadata) = MetadataManager::get_instance(MetadataType::Do)
//         //     .get_metadata(srv_ctx, ref_id)
//         //     .await?
//         // {

//         // }

//         do_metadata.set_ref_do_id(ref_id.to_owned());
//         Ok(())
//     }
// }
